iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Build on Google AI

30 天打造 AI Web Security Agent:從 Google AI Studio 到 Gemini Agent系列 第 19 篇

Day 19|把留言的跨請求路徑交給 AI,它能分清「已儲存」與「已執行」嗎?

  • 分享至 

  • xImage
  •  

前言

Day 18,我把同一種固定探針送進 3 個本機留言表單。原樣插入組保存留言,後續讀取頁把它當作 HTML,Chrome 建立圖片並出現 alert(1) 與相符 DOM 旗標;跳脫組也保存留言,但只把它作為文字顯示;拒絕組回傳 422,根本沒有保存。今天我不重新送出探針,而是讀取已保存的表單、HTTP 與瀏覽器結果,檢查 AI 風險報告能否保留這條跨請求資料流。

儲存型 XSS 的關鍵在於輸入可以先被網站保存,再由後續頁面帶給讀者。MDN 以留言功能說明這種流程;但 Day 18 的伺服器只在記憶體中保存資料,我沒有測試資料庫、跨帳號讀取或真實使用者。

五份來源,對應五個判讀位置

我沿用 Day 7 的九欄報告結構,但把 tool_verdict 限定為 Day 18 的 3 種結果:confirmed_stored_xss、stored_as_text 和 submission_rejected。每組報告收到 E1~E5 五份具名證據:

來源 提供給模型的事實
E1 表單 method、action、欄位與 Day 18 工具分類
E2 獨立 POST 的目標、狀態與提交回應
E3 伺服器是否保存,以及後續 GET 的原始 HTML
E4 Chrome 中實際形成的 DOM、對話框與相符旗標
E5 本機實驗的適用範圍

這個順序有助於避免判讀跳步。post_status: 201 表示這次提交成功,還不是執行證據;storage_matches_payload: true 表示伺服器保存了完整輸入,仍需查看讀取頁怎麼顯示。弱點組最後的關鍵幾行是:

E2  post_status: 201
E3  storage_matches_payload: true
E3  read_response_contains_token: true
E3  read_response_has_escaped_tag: false
E4  img_created: true
E4  alert_observed: true
E4  dialog_message: 1
E4  dom_marker_matches_token: true
E4  execution_observed: true

跳脫組同樣是 201 且保存了輸入,但 E3 的 read_response_has_escaped_tag: true,E4 沒有建立圖片或執行訊號。拒絕組則在 E2 回傳 422;E3 的儲存值是 null,後續 GET 不含探針。這兩種未執行結果的原因不同,不能合成一個籠統的「安全」。

先核對原始檔,再產生報告

我先執行乾跑:

.venv/bin/python day-19/run_reports.py --dry-run

程式重新讀取 Day 18 的完整表單 HTML、POST 與 GET 原始 HTTP、讀取頁原始內容和各檔 SHA-256,並核對伺服器儲存狀態、Chrome DOM、雙重訊號及摘要。乾跑三組都通過,但明確記錄 api_called: false,沒有模型回答。這次沒有重啟本機網站,也沒有再次提交 XSS 探針。

我另外執行 6 個離線測試,包括竄改表單、POST、GET、儲存狀態、瀏覽器訊號及摘要;這些修改都會被拒絕。測試也檢查報告缺少必要引用、擅自評成 high,或替未確認組填入影響與修正建議時,不能通過自動驗證。

接著我用 gemini-3.5-flash-lite 對三組證據各送出一次請求,保存來源副本、提示、API 回應、模型原始文字與九欄 JSON。三組都通過 Schema、分類、必要逐字引用和本機評級限制。

組別 Day 18 分類 模型分類 模型保留的差異 自動檢查
原樣插入 confirmed_stored_xss confirmed_stored_xss POST 保存,後續 GET 未跳脫,Chrome 有雙重訊號 通過
HTML 跳脫 stored_as_text stored_as_text 已保存,但後續作文字顯示 通過
拒絕提交 submission_rejected submission_rejected 422 且未保存,讀取頁無探針 通過

三份報告的 risk_rating 都是 needs_context。跳脫組和拒絕組的 possible_impacts、remediation 都是空陣列。這個評級表示缺少真實資產、使用者權限與業務背景;不能把它解讀成「低風險」。

分類保住了,文字仍要收斂

弱點組的模型摘要依序描述表單 POST、201 保存、後續 GET 未跳脫、Chrome 建立圖片,以及 alert(1) 和相符 DOM 旗標,與 Day 18 的資料一致。它在可能影響中寫「執行預期外的腳本程式碼」;這個方向可以提醒我評估,但本次直接證明的只有固定 img onerror 事件處理程式在本機讀取頁執行。我沒有測試任意腳本、真實使用者或正式網站,因此不把較廣的影響措辭當成本文結論。

模型建議在 HTML 文字位置做輸出編碼,符合這個純文字留言頁的需求;OWASP 也指出,讓輸入作為資料顯示時,編碼必須符合所在的輸出情境。

跳脫組的模型說明有保存、經跳脫、只顯示文字,與本次觀察相符;「正確轉義」只能限於這個 HTML 文字位置。拒絕組的模型說「未發生弱點利用」,我將它收斂成「這次提交被拒絕、沒有保存,也沒有觀察到本次探針執行」,不能推論其他輸入或整站安全。模型原文保持不變,作者核對另存於 Day 19 實測目錄。

Day 19:報告要說出資料何時進入頁面

三份模型報告都保留了 Day 18 的技術分類,也沒有把「已保存」直接寫成「已執行」。表單、POST、儲存、後續 GET、Chrome 的分層來源,讓我能逐段檢查模型用了哪份證據。但 Schema 和引用只能守住格式與部分事實,自由文字的影響範圍仍需要對照原始實驗。

本次結論只限於 127.0.0.1 的記憶體留言、單一 message 欄位、固定圖片錯誤事件探針與三種對照處理;沒有測試持久資料庫、跨帳號傳播、其他瀏覽器、CSP 或正式環境。

那就…
明天見!


上一篇
Day 18|留言先存起來,下一次開頁面才執行:我怎麼確認儲存型 XSS?
下一篇
Day 20|把五種 XSS 情境排成一張可追溯的證據表
系列文
30 天打造 AI Web Security Agent:從 Google AI Studio 到 Gemini Agent 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言